package np.com.abhishek.sudoku.ui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Vector;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;

import np.com.abhishek.ui.WidgetFactory;

public class Board extends JFrame implements ActionListener {
    JPanel pnlCell, pnlButtons;

    JButton btnSolve, btnReset, btnClear, btnExit;

    JLabel lblImgHolder;

    Cell cell[][];

    int initFilledCells, filledCells;

    Vector<Integer> vp, hp, sp, temp1, temp2, temp3, common;
    Vector<Integer>[][] possibilities;

    boolean possiblized[][], possiblized2[][], possiblized3[][];

    private static final long serialVersionUID = 2L;
    
    WidgetFactory widgetFactory = WidgetFactory.getInstance();

    public Board() {
	Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

	pnlCell = new JPanel();
	pnlButtons = new JPanel();
	lblImgHolder = new JLabel(new ImageIcon(widgetFactory.createImage("bg.jpg")));

	cell = new Cell[9][9];
	initFilledCells = 0;
	filledCells = 0;
	possibilities = new Vector[10][10];
	possiblized = new boolean[9][9];
	possiblized2 = new boolean[9][9];
	possiblized3 = new boolean[9][9];

	btnSolve = new JButton("Solve");
	btnExit = new JButton("Exit");
	btnReset = new JButton("Reset");
	btnClear = new JButton("Clear");

	getRootPane().setDefaultButton(btnSolve);
	btnSolve.addActionListener(this);
	btnReset.addActionListener(this);
	btnClear.addActionListener(this);
	btnExit.addActionListener(this);

	lblImgHolder.setBorder(new TitledBorder(""));
	pnlButtons.setBorder(new TitledBorder(""));

	pnlCell.setBounds(5, 5, 425, 425);
	pnlButtons.setBounds(5, 435, 425, 60);
	lblImgHolder.setBounds(0, 0, 425, 425);

	pnlCell.setLayout(null);
	pnlButtons.setLayout(new GridLayout(2, 3, 10, 5));
	lblImgHolder.setLayout(new GridLayout(9, 9, 8, 8));

	pnlCell.add(lblImgHolder);

	pnlButtons.add(btnSolve);
	pnlButtons.add(btnReset);
	pnlButtons.add(btnClear);
	pnlButtons.add(btnExit);

	add(pnlCell);
	add(pnlButtons);

	setTitle("Sudoku Solver(c)2007 Abhishek Shrestha");
	setSize(443, 533);
	setLayout(null);
	setIconImage(widgetFactory.createImage("icon.gif"));
	setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	int x = (screenSize.width - getWidth()) / 2;
	int y = (screenSize.height - getHeight()) / 2;

	setLocation(x, y);
	setResizable(false);
	addCell();
	setVisible(true);
    }

    private void addCell() {
	for (int i = 0; i < 9; i++) {
	    for (int j = 0; j < 9; j++) {
		cell[i][j] = new Cell();
		lblImgHolder.add(cell[i][j]);
		possiblized[i][j] = false;
		possiblized2[i][j] = false;
		possiblized3[i][j] = false;
	    }
	}
    }

    void countFilledCells() {
	for (int i = 0; i < 9; i++) {
	    for (int j = 0; j < 9; j++) {
		possibilities[i][j] = new Vector<Integer>(0);
		if (cell[i][j].getValue() > 0) {
		    possibilities[i][j].addElement(cell[i][j].getValue());
		    filledCells++;
		}
	    }
	}
	System.out.println("Filled Count: " + filledCells);
    }

    public void actionPerformed(ActionEvent ae) {
	if (ae.getSource() == btnSolve) {
	    if (preSolve()) {
		countFilledCells();
		solve();
		initFilledCells = filledCells;
		btnSolve.setEnabled(false);

		if (filledCells < 81)
		    singletonInRCS();

		if (filledCells < 81)
		    findRowColRemains();

		if (filledCells < 81)
		    findHiddenLoner();

		for (int i = 0; i < 9; i++) {
		    System.out
			    .println("[" + i + "][9]: " + possibilities[i][9]);
		    System.out
			    .println("[9][" + i + "]: " + possibilities[9][i]);
		}
	    }
	}

	if (ae.getSource() == btnClear) {
	    clear();
	}

	if (ae.getSource() == btnReset) {
	    reset();
	}

	if (ae.getSource() == btnExit) {
	    dispose();
	}

    }

    void clear() {
	for (int i = 0; i < 9; i++) {
	    for (int j = 0; j < 9; j++) {
		cell[i][j].emptyCell();
		initFilledCells = 0;
		filledCells = 0;
	    }
	}
    }

    boolean preSolve() {
	if (checkValidity()) {
	    JOptionPane.showMessageDialog(null,
		    "Duplicate entry found \n Removing the repitition.",
		    "Removing Repitition(s)", JOptionPane.INFORMATION_MESSAGE);
	    clearRepetition();
	    return false;
	} else
	    return true;
    }

    /*
     * is invoked only when for the first time the SOLVE button is pressed thus
     * is used for checking the values on mass scale once the repititions are
     * found then they are highlighted in green and with user's permission they
     * are removed from the boxes/cells
     */
    boolean checkValidity() {
	/*
	 * param @ isRepeatedV flags if found repitition in Vertical direction
	 * param @ isRepeatedH flags if found repitition in Horizontal direction
	 * param @ isRepeatedS flags if found repitition in Square (range)
	 */
	boolean isRepeatedV = false, isRepeatedH = false, isRepeatedS = false;

	/*
	 * working with the 3x3 square (range) values and range begins at
	 * indices like [0][0] [0][3] [0][6] [3][0] [3][3] [3][6] [6][0] [6][3]
	 * [6][6] this is what exactly the first two for loop does then [0][0]
	 * is checked with [1][0] [1][1] [1][2] [2][0] [2][1] [2][2] then [0][1]
	 * is checked with [1][0] [1][1] [1][2] [2][0] [2][1] [2][2] then [0][2]
	 * is checked with [1][0] [1][1] [1][2] [2][0] [2][1] [2][2] then [1][0]
	 * is checked with [2][0] [2][1] [2][2] then [1][1] is checked with
	 * [2][0] [2][1] [2][2] then [1][2] is checked with [2][0] [2][1] [2][2]
	 * and so on... * PLEASE EXECUTE FILE repititionCheck.java TO SEE THE
	 * TEST CASE
	 */
	for (int r = 0; r <= 6; r += 3) {
	    for (int c = 0; c <= 6; c += 3) {
		for (int i = r; i < r + 2; i++) {
		    for (int j = c; j <= c + 2; j++) {
			for (int x = i + 1; x <= r + 2; x++) {
			    for (int y = c; y <= c + 2; y++) {
				if ((cell[i][j].getValue() == cell[x][y]
					.getValue())
					&& (cell[i][j].getValue() != 0)) {
				    // searching for duplicate in square
				    cell[x][y].setForeground(Color.green);
				    cell[i][j].setForeground(Color.green);
				    isRepeatedS = true;
				}
			    }

			}
		    }
		}
	    }
	}
	/*
	 * working with the row and column values since [0][0] has already been
	 * checked with [0][1] [0][2] in range repitition check thus we only
	 * need to check [0][3] through [0][8] in horizontal and [3][0] through
	 * [8][0] in vertical for [0][3] to [0][5] we need to check only [0][6]
	 * [0][7] [0][8] horizontally and [3][3] through [8][3] vertically and
	 * for last horizontal range only vertical check is necessary also for
	 * last vertical range only horizontal check is necesary PLEASE EXECUTE
	 * FILE repititionCheck.java TO SEE THE TEST CASE
	 */
	for (int i = 0; i < 9; i++) {
	    for (int j = 0; j < 9; j++) {
		for (int k = 0; k < 9; k++) {
		    if (k != j
			    && (cell[i][j].getValue() == cell[i][k].getValue())
			    && (cell[i][j].getValue() > 0)) {
			cell[i][k].setForeground(Color.green);
			cell[i][j].setForeground(Color.green);
			isRepeatedH = true;
		    } // end of row search
		    // searching for duplicate in column
		    if (k != i
			    && (cell[i][j].getValue() == cell[k][j].getValue())
			    && (cell[k][j].getValue() > 0)) {
			cell[k][j].setForeground(Color.green);
			cell[i][j].setForeground(Color.green);
			isRepeatedV = true;
		    } // end of column search
		}
	    }
	}

	if (isRepeatedV || isRepeatedH || isRepeatedS)
	    return true;
	else
	    return false;
    }

    void clearRepetition() {
	for (int i = 0; i < 9; i++) {
	    for (int j = 0; j < 9; j++) {
		if (cell[i][j].getForeground().equals(Color.green)) {
		    cell[i][j].emptyCell();
		    cell[i][j].setForeground(Color.red);
		}
	    }
	}
    }

    void reset() {
	filledCells = initFilledCells;
	for (int i = 0; i < 9; i++) {
	    for (int j = 0; j < 9; j++) {
		if (!(cell[i][j].getForeground().equals(Color.red))
			|| (cell[i][j].getForeground().equals(Color.black))) {
		    cell[i][j].setText("");
		    cell[i][j].setForeground(Color.black);
		    possiblized[i][j] = false;
		}
	    }
	}

	btnSolve.setEnabled(true);
    }

    void solve() {
	System.out.println("Solving with phase 1 .... " + filledCells);
	int oldFilledCells = filledCells;
	while (filledCells < 81) {
	    int tempFilledCells = filledCells;
	    for (int i = 0; i < 9; i++) {
		for (int j = 0; j < 9; j++) {
		    if (cell[i][j].getValue() == 0)
			checkPossibility(i, j);
		    else {
			possibilities[i][j] = new Vector<Integer>(0);
			possibilities[i][j].addElement(new Integer(cell[i][j]
				.getValue()));
		    }

		}
	    }
	    if (filledCells == tempFilledCells) // to avoid infinite loop
		break;
	}
	if (filledCells == 81)
	    JOptionPane.showMessageDialog(this, "Solved!", "Sudoku Solver",
		    JOptionPane.INFORMATION_MESSAGE);
	else if (oldFilledCells == filledCells)
	    System.out.println("No progress made by level 1.....");
	else if (oldFilledCells < filledCells) {
	    int diff = filledCells - oldFilledCells;
	    System.out.println("No. of boxes filled by phase 1: " + diff);
	}
    }

    void checkPossibility(int r, int c) {
	vp = new Vector<Integer>(0);
	hp = new Vector<Integer>(0);
	sp = new Vector<Integer>(0);

	temp1 = new Vector<Integer>(0);
	temp2 = new Vector<Integer>(0);
	temp3 = new Vector<Integer>(0);
	common = new Vector<Integer>(0);

	possibilities[r][9] = new Vector<Integer>(0);
	possibilities[9][c] = new Vector<Integer>(0);

	for (int i = 0; i < 9; i++) {
	    if (cell[r][i].getValue() != 0) // adding user supplied row's
		// data
		temp1.addElement(new Integer(cell[r][i].getValue()));

	    if (cell[i][c].getValue() != 0) // adding user supplied cloumn's
		// data
		temp2.addElement(new Integer(cell[i][c].getValue()));
	}

	/*
	 * checks for the remainig values i.e. if user has supplied 1,2,3,4,5,9
	 * then the remaining values are 6, 7, 8 that can be filled in the
	 * reaminig squares
	 */
	for (int i = 0; i < 9; i++) {
	    if (!temp1.contains(i + 1)) {
		hp.addElement(i + 1); // for row's data
		possibilities[r][9].addElement(i + 1);
	    }

	    if (!temp2.contains(i + 1)) {
		vp.addElement(i + 1); // for column's data
		possibilities[9][c].addElement(i + 1);
	    }
	}

	// working with the square values
	// the following two lines decodes the starting coordinate of the square
	int startRow = (r / 3) * 3;
	int startCol = (c / 3) * 3;

	for (int i = startRow; i < startRow + 3; i++) {
	    for (int j = startCol; j < startCol + 3; j++) {
		if (cell[i][j].getValue() != 0) // adding user supplied squares'
		    // data
		    temp3.addElement(new Integer(cell[i][j].getValue()));
	    }
	}

	int num = 0; // since numbers to be filled are 1...9 so lets start
	// checking with 1
	for (int i = startRow; i < startRow + 3; i++) {
	    for (int j = startCol; j < startCol + 3; j++) {
		num++;
		if (!temp3.contains(num))
		    sp.addElement(num);
	    }
	}
	findLeast(r, c);
    }

    // this function optimizes the program and nothing else
    void findLeast(int r, int c) {
	Integer n = 0;
	int cellLst[] = { hp.size(), vp.size(), sp.size() };
	Arrays.sort(cellLst);
	int smallest = cellLst[0];

	if (smallest == hp.size()) {
	    for (int i = 0; i < hp.size(); i++) {
		int val = hp.get(i);
		n = new Integer(val);

		if (vp.contains(n) && sp.contains(n))
		    common.addElement(n);
	    }
	} else if (smallest == vp.size()) {
	    for (int i = 0; i < vp.size(); i++) {
		int val = vp.get(i);
		n = new Integer(val);

		if (sp.contains(n) && hp.contains(n))
		    common.addElement(n);
	    }
	} else if (smallest == sp.size()) {
	    for (int i = 0; i < sp.size(); i++) {
		int val = sp.get(i);
		n = new Integer(val);

		if (hp.contains(n) && vp.contains(n))
		    common.addElement(n);
	    }
	}

	possibilities[r][c] = new Vector<Integer>(common.size());
	possibilities[r][c] = common;

	if (common.size() == 1) { // sure shot spots
	    int txtVal = common.get(0);

	    eliminate(r, c, txtVal);
	    deleteRCSDuplicate(r, c, txtVal);
	    System.out.println("Filled with phase 1: [" + r + ", " + c + "]: "
		    + txtVal);
	} else if (common.size() > 1) {
	    // Listing out the possibilities
	    possiblized[r][c] = true;

	    cell[r][c].setFont(new Font("Times New Roman", Font.BOLD, 12));
	    cell[r][c].setForeground(Color.gray);
	    cell[r][c].setText("" + possibilities[r][c]);
	}
    }

    void singletonInRCS() {
	// System.out.println("Unable to complete with phase 1.... \n");
	System.out.println("\nSolving with phase 2 .... \n");
	int oldFilledCount = filledCells;
	int testNum = 0, q = 0, HrepeatedCounter = 0, VrepeatedCounter = 0, SrepeatedCounter = 0;

	while (filledCells < 81) {
	    int tempFilledCells = filledCells;
	    for (int i = 0; i < 9; i++) {
		for (int j = 0; j < 9; j++) {
		    if (possibilities[i][j].size() > 1) {
			int index = 0;
			index = possibilities[i][j].size();

			for (int p = 0; p < index; p++) {
			    HrepeatedCounter = 0;
			    VrepeatedCounter = 0;
			    SrepeatedCounter = 0;

			    testNum = possibilities[i][j].get(p);

			    for (q = 0; q < 9; q++) {
				if (possibilities[i][j].size() > 1) { // for
				    // row
				    if (possibilities[i][q]
					    .contains(new Integer(testNum)))
					HrepeatedCounter++;
				}

				if (possibilities[i][j].size() > 1) { // for
				    // column
				    if (possibilities[q][j]
					    .contains(new Integer(testNum))) {
					VrepeatedCounter++;
				    }
				}
			    }
			    int m = (i / 3) * 3;
			    int n = (j / 3) * 3;
			    for (int r = m; r < m + 3; r++) {
				for (int c = n; c < n + 3; c++) {
				    if (possibilities[i][j].size() > 1) { // for
					// square/
					// region
					if (possibilities[r][c]
						.contains(new Integer(testNum)))
					    SrepeatedCounter++;
				    }
				}
			    }

			    if (HrepeatedCounter == 1 || VrepeatedCounter == 1
				    || SrepeatedCounter == 1) {
				if (HrepeatedCounter == 1)
				    System.out
					    .println("Horizontal Singleton found @ ["
						    + i
						    + "]["
						    + j
						    + "]:"
						    + testNum);
				else if (VrepeatedCounter == 1)
				    System.out
					    .println("Vertical Singleton found @ ["
						    + i
						    + "]["
						    + j
						    + "]:"
						    + testNum);
				else
				    // (SrepeatedCounter == 1)
				    System.out
					    .println("Square Singleton found @ ["
						    + i
						    + "]["
						    + j
						    + "]:"
						    + testNum);
				eliminate(i, j, testNum);
				deleteRCSDuplicate(i, j, testNum);
				System.out.println("Filled with phase 2: [" + i
					+ ", " + j + "]: " + testNum);
				solve();
				break;
			    }
			}
		    }
		}
	    }
	    if (filledCells == tempFilledCells) // to avoid infinite loop
		break;
	}
	if (oldFilledCount == filledCells)
	    System.out.println("No progress made by phase 2.....");
	else if (oldFilledCount < filledCells) {
	    // int diff = filledCells - oldFilledCount;
	    // System.out.println("No. of boxes filled by phase 2: " +
	    // diff);
	}
    }

    void findRowColRemains() {
	Integer existing = 0;
	System.out.println("\n- - - Solving with phase 3 .... \n");
	System.out.println("Cells Filled: " + filledCells);
	int oldFilledCount = filledCells;

	while (filledCells < 81) {
	    int tempFilledCells = filledCells;

	    for (int i = 0; i < 9; i++) {
		// row remains
		for (int index = 0; index < possibilities[i][9].size(); index++) {
		    existing = possibilities[i][9].get(index);
		    // System.out.println("\nChecking with index [" + index
		    // + "]: " + existing);
		    // for range starting with [0][0] [0][3] [0][6]
		    if ((possibilities[i][0].contains(existing) || possibilities[i][0]
			    .size() == 1)
			    && (possibilities[i][1].contains(existing) || possibilities[i][1]
				    .size() == 1)
			    && (possibilities[i][2].contains(existing) || possibilities[i][2]
				    .size() == 1)) {

			if (!(possibilities[i][3].contains(existing)
				|| possibilities[i][4].contains(existing)
				|| possibilities[i][5].contains(existing)
				|| possibilities[i][6].contains(existing)
				|| possibilities[i][7].contains(existing) || possibilities[i][8]
				.contains(existing))) {
			    // System.out.println(existing + " , thus
			    // remains only in 0 Row " + i );
			    eraseRemains(false, i, 0, existing);
			}
		    }
		    // for range starting with [0][0] [0][3] [0][6]
		    else if ((possibilities[i][3].contains(existing) || possibilities[i][3]
			    .size() == 1)
			    && (possibilities[i][4].contains(existing) || possibilities[i][4]
				    .size() == 1)
			    && (possibilities[i][5].contains(existing) || possibilities[i][5]
				    .size() == 1)) {

			if (!(possibilities[i][0].contains(existing)
				|| possibilities[i][1].contains(existing)
				|| possibilities[i][2].contains(existing)
				|| possibilities[i][6].contains(existing)
				|| possibilities[i][7].contains(existing) || possibilities[i][8]
				.contains(existing))) {
			    // System.out.println(existing + " , thus
			    // remains only in 3 Row " + i );
			    eraseRemains(false, i, 3, existing);
			}
		    }
		    // for range starting with [0][0] [0][3] [0][6]
		    else if ((possibilities[i][6].contains(existing) || possibilities[i][6]
			    .size() == 1)
			    && (possibilities[i][7].contains(existing) || possibilities[i][7]
				    .size() == 1)
			    && (possibilities[i][8].contains(existing) || possibilities[i][8]
				    .size() == 1)) {

			if (!(possibilities[i][0].contains(existing)
				|| possibilities[i][1].contains(existing)
				|| possibilities[i][2].contains(existing)
				|| possibilities[i][3].contains(existing)
				|| possibilities[i][4].contains(existing) || possibilities[i][5]
				.contains(existing))) {
			    // System.out.println(existing + " , thus
			    // remains only in 6 Row " + i );
			    eraseRemains(false, i, 6, existing);
			}
		    }
		}
		// column remains
		for (int index = 0; index < possibilities[9][i].size(); index++) {
		    // System.out.println("\nPossibilities: " +
		    // possibilities[9][i]);
		    existing = possibilities[9][i].get(index);
		    // System.out.println("\nChecking with index [" + index
		    // + "]: " + existing);
		    // for range starting with [0][0] [0][3] [0][6]
		    if ((possibilities[0][i].contains(existing) || possibilities[0][i]
			    .size() == 1)
			    || (possibilities[1][i].contains(existing) || possibilities[1][i]
				    .size() == 1)
			    || (possibilities[2][i].contains(existing) || possibilities[2][i]
				    .size() == 1)) {
			// System.out.println("Found: "+ existing);
			if (!(possibilities[3][i].contains(existing)
				|| possibilities[4][i].contains(existing)
				|| possibilities[5][i].contains(existing)
				|| possibilities[6][i].contains(existing)
				|| possibilities[7][i].contains(existing) || possibilities[8][i]
				.contains(existing))) {
			    // System.out.println(existing + " , thus
			    // remains only in 0 Col " + i);
			    eraseRemains(true, 0, i, existing);
			}
		    }
		    // for range starting with [0][0] [0][3] [0][6]
		    else if ((possibilities[3][i].contains(existing) || possibilities[3][i]
			    .size() == 1)
			    || (possibilities[4][i].contains(existing) || possibilities[4][i]
				    .size() == 1)
			    || (possibilities[5][i].contains(existing) || possibilities[5][i]
				    .size() == 1)) {
			if (!(possibilities[0][i].contains(existing)
				|| possibilities[1][i].contains(existing)
				|| possibilities[2][i].contains(existing)
				|| possibilities[6][i].contains(existing)
				|| possibilities[7][i].contains(existing) || possibilities[8][i]
				.contains(existing))) {
			    // System.out.println(existing + " , thus
			    // remains only in 3 Col " + i);
			    eraseRemains(true, 3, i, existing);
			}
		    }
		    // for range starting with [0][0] [0][3] [0][6]
		    else if ((possibilities[6][i].contains(existing) || possibilities[6][i]
			    .size() == 1)
			    || (possibilities[7][i].contains(existing) || possibilities[7][i]
				    .size() == 1)
			    || (possibilities[8][i].contains(existing) || possibilities[8][i]
				    .size() == 1)) {
			if (!(possibilities[0][i].contains(existing)
				|| possibilities[1][i].contains(existing)
				|| possibilities[2][i].contains(existing)
				|| possibilities[3][i].contains(existing)
				|| possibilities[4][i].contains(existing) || possibilities[5][i]
				.contains(existing))) {
			    // System.out.println(existing + " , thus
			    // remains only in 6 Col " + i);
			    eraseRemains(true, 6, i, existing);
			}
		    }
		}
	    }
	    singletonInRCS();
	    if (filledCells == tempFilledCells) // to avoid infinite loop
		break;
	}
	if (oldFilledCount == filledCells)
	    System.out.println("No progress made by phase 3.....");
	else if (oldFilledCount < filledCells) {
	    int diff = filledCells - oldFilledCount;
	    System.out.println("No. of boxes filled by phase 3: " + diff);
	}
    }

    void eraseRemains(boolean FlagRowOrCol, int rowStart, int colStart,
	    int current) {
	/*
	 * if FlagRowOrCol = 0 (false) then row has called if FlagRowOrCol = 1
	 * (true) then col has called
	 */
	int x = 0, y = 0;
	if (!FlagRowOrCol) {// if row
	    x = (rowStart / 3) * 3;
	    y = colStart;
	    for (int r = x; r < x + 3; r++) {
		for (int c = y; c < y + 3; c++) {
		    if (r != rowStart)
			findDuplicate(r, c, current);
		}
	    }
	} else {// col
	    x = rowStart;
	    y = (colStart / 3) * 3;
	    for (int r = x; r < x + 3; r++) {
		for (int c = y; c < y + 3; c++) {
		    if (c != colStart)
			findDuplicate(r, c, current);
		}
	    }
	}
    }

    void findDuplicate(int r, int c, int current) {
	if (possibilities[r][c].contains(current)
		&& possibilities[r][c].size() > 1) {
	    // System.out.println("Removing " + current + "from ["+ r + "]["
	    // + c + "]");
	    possibilities[r][c].remove(new Integer(current));

	    cell[r][c].setText("");
	    cell[r][c].setText("" + possibilities[r][c]);

	    if (possibilities[r][c].size() == 1) {
		// System.out.println("findDup: " + possibilities[r][c]);
		eliminate(r, c, possibilities[r][c].get(0));
		deleteRCSDuplicate(r, c, possibilities[r][c].get(0));
	    }
	}
    }

    void deleteRCSDuplicate(int r, int c, int cellVal) {
	for (int q = 0; q < 9; q++) { // removal from row
	    if (possibilities[r][q].size() > 1 && c != q
		    && possibilities[r][q].contains(new Integer(cellVal))) { // for
		// row
		possibilities[r][q].remove(new Integer(cellVal));
		// System.out.println("Placing " + possibilities[r][q] + " in ["
		// + r + "][" + q + "]");
		cell[r][q].setText("" + possibilities[r][q]);

		if (possibilities[r][q].size() == 1) {
		    // System.out.println("deleteRCS: " +
		    // possibilities[r][q]);
		    eliminate(r, q, possibilities[r][q].get(0));
		}
	    }
	    if (possibilities[q][c].size() > 1 && r != q
		    && possibilities[q][c].contains(new Integer(cellVal))) { // for
		// col
		possibilities[q][c].remove(new Integer(cellVal));
		// System.out.println("Placing " + possibilities[q][c] + " in ["
		// + q + "][" + c + "]");
		cell[q][c].setText("" + possibilities[q][c]);

		if (possibilities[q][c].size() == 1) {
		    // System.out.println("deleteRCS: "+
		    // possibilities[q][c]);
		    eliminate(q, c, possibilities[q][c].get(0));
		}
	    }
	}
	int m = (r / 3) * 3;
	int n = (c / 3) * 3;
	for (int i = m; i < m + 3; i++) {
	    for (int j = n; j < n + 3; j++) {
		if (possibilities[i][j].size() > 1
			&& possibilities[i][j].contains(new Integer(cellVal))
			&& i != r && j != c) { // for range
		    possibilities[i][j].remove(new Integer(cellVal));
		    // System.out.println("Placing " + possibilities[i][j] +
		    // " in [" + i + "][" + j + "]");
		    // System.out.println("Removing " + cellVal + " from ["
		    // + i + "][" + j + "]");
		    cell[i][j].setText("" + possibilities[i][j]);

		    if (possibilities[i][j].size() == 1) {
			// System.out.println("deleteRCS: " +
			// possibilities[i][j]);
			eliminate(i, j, possibilities[i][j].get(0));
		    }
		}
	    }
	}
    }

    void eliminate(int r, int c, int cellVal) {
	possibilities[r][9].remove(new Integer(cellVal));
	// System.out.println("Removing " + cellVal + " from possibilities[" + r
	// + "][9]");
	possibilities[9][c].remove(new Integer(cellVal));
	// System.out.println("Removing " + cellVal + " from possibilities[9]["
	// + c + "]");
	if (cell[r][c].getValue() == 0) {
	    System.out.println("[" + r + "][" + c + "]: " + cellVal);
	    cell[r][c].putValue(cellVal);
	    filledCells++;
	    System.out.println("Filled cells: " + filledCells);
	    // System.out.println("Placing " + cellVal + " in [" + r + "]["
	    // + c + "]");
	}

    }

    void findHiddenLoner() {
	System.out.println(" - - - Solving with phase 4 .... \n");
	System.out.println("Cells Filled: " + filledCells);

	int oldFilledCount = filledCells;
	while (filledCells < 81) {
	    int tempFilledCells = filledCells;
	    Integer num = 0;
	    for (int r = 0; r < 9; r++) {
		for (int c = 0; c <= 6; c += 3) {
		    for (int index = 0; index < possibilities[r][9].size(); index++) {
			num = possibilities[r][9].get(index);

			/*
			 * for row numbers like 0, 3, 6 we have to make sure
			 * that
			 */
			if (r % 3 == 0) {
			    if ((((possibilities[r][c].contains(num) && possibilities[r][c]
				    .size() >= 2) && (possibilities[r][c + 1]
				    .contains(num) && possibilities[r][c + 1]
				    .size() >= 2))
				    || ((possibilities[r][c + 1].contains(num) && possibilities[r][c + 1]
					    .size() >= 2) && (possibilities[r][c + 2]
					    .contains(num) && possibilities[r][c + 2]
					    .size() >= 2)) || ((possibilities[r][c + 2]
				    .contains(num) && possibilities[r][c + 2]
				    .size() >= 2) && (possibilities[r][c]
				    .contains(num) && possibilities[r][c]
				    .size() >= 2)))
				    && !((possibilities[r + 1][c].contains(num) && possibilities[r + 1][c]
					    .size() >= 2)
					    || (possibilities[r + 1][c + 1]
						    .contains(num) && possibilities[r + 1][c + 1]
						    .size() >= 2)
					    || (possibilities[r + 1][c + 2]
						    .contains(num) && possibilities[r + 1][c + 2]
						    .size() >= 2)
					    || (possibilities[r + 2][c]
						    .contains(num) && possibilities[r + 2][c]
						    .size() >= 2)
					    || (possibilities[r + 2][c + 1]
						    .contains(num) && possibilities[r + 2][c + 1]
						    .size() >= 2) || (possibilities[r + 2][c + 2]
					    .contains(num) && possibilities[r + 2][c + 2]
					    .size() >= 2))) {
				for (int i = 0; i < 9; i++) {
				    if (i != c && i != c + 1 && i != c + 2) {
					// System.out.println("Removing " + num
					// + " from [" + r + "][" + i + "]");
					possibilities[r][i].remove(new Integer(
						num));
					if (possibilities[r][i].size() == 1) {
					    System.out
						    .println("hiddenLoner found: @["
							    + r
							    + "]["
							    + i
							    + "]: "
							    + possibilities[r][i]
								    .get(0));
					    eliminate(r, i, possibilities[r][i]
						    .get(0));
					    deleteRCSDuplicate(r, i,
						    possibilities[r][i].get(0));
					}
				    }
				}
			    }
			}

			// for row numbers like 1, 4, 7
			else if (r % 3 == 1) {
			    // System.out.println("for [" + r + "][" + c +
			    // "]: " + num);
			    if ((((possibilities[r][c].contains(num) && possibilities[r][c]
				    .size() >= 2) && (possibilities[r][c + 1]
				    .contains(num) && possibilities[r][c + 1]
				    .size() >= 2))
				    || ((possibilities[r][c + 1].contains(num) && possibilities[r][c + 1]
					    .size() >= 2) && (possibilities[r][c + 2]
					    .contains(num) && possibilities[r][c + 2]
					    .size() >= 2)) || ((possibilities[r][c + 2]
				    .contains(num) && possibilities[r][c + 2]
				    .size() >= 2) && (possibilities[r][c]
				    .contains(num) && possibilities[r][c]
				    .size() >= 2)))
				    && !((possibilities[r - 1][c].contains(num) && possibilities[r - 1][c]
					    .size() >= 2)
					    || (possibilities[r - 1][c + 1]
						    .contains(num) && possibilities[r - 1][c + 1]
						    .size() >= 2)
					    || (possibilities[r - 1][c + 2]
						    .contains(num) && possibilities[r - 1][c + 2]
						    .size() >= 2)
					    || (possibilities[r + 1][c]
						    .contains(num) && possibilities[r + 1][c]
						    .size() >= 2)
					    || (possibilities[r + 1][c + 1]
						    .contains(num) && possibilities[r + 1][c + 1]
						    .size() >= 2) || (possibilities[r + 1][c + 2]
					    .contains(num) && possibilities[r + 1][c + 2]
					    .size() >= 2))) {
				for (int i = 0; i < 9; i++) {
				    if (i != c && i != c + 1 && i != c + 2) {
					// System.out.println("Removing " + num
					// + " from [" + r + "][" + i + "]");
					possibilities[r][i].remove(new Integer(
						num));
					if (possibilities[r][i].size() == 1) {
					    System.out
						    .println("hiddenLoner found: @["
							    + r
							    + "]["
							    + i
							    + "]: "
							    + possibilities[r][i]
								    .get(0));
					    eliminate(r, i, possibilities[r][i]
						    .get(0));
					    deleteRCSDuplicate(r, i,
						    possibilities[r][i].get(0));
					}
				    }
				}
			    }
			}
			// for row numbers like 2, 5, 8
			else if (r % 3 == 2) {
			    if ((((possibilities[r][c].contains(num) && possibilities[r][c]
				    .size() >= 2) && (possibilities[r][c + 1]
				    .contains(num) && possibilities[r][c + 1]
				    .size() >= 2))
				    || ((possibilities[r][c + 1].contains(num) && possibilities[r][c + 1]
					    .size() >= 2) && (possibilities[r][c + 2]
					    .contains(num) && possibilities[r][c + 2]
					    .size() >= 2)) || ((possibilities[r][c + 2]
				    .contains(num) && possibilities[r][c + 2]
				    .size() >= 2) && (possibilities[r][c]
				    .contains(num) && possibilities[r][c]
				    .size() >= 2)))
				    && !((possibilities[r - 1][c].contains(num) && possibilities[r - 1][c]
					    .size() >= 2)
					    || (possibilities[r - 1][c + 1]
						    .contains(num) && possibilities[r - 1][c + 1]
						    .size() >= 2)
					    || (possibilities[r - 1][c + 2]
						    .contains(num) && possibilities[r - 1][c + 2]
						    .size() >= 2)
					    || (possibilities[r - 2][c]
						    .contains(num) && possibilities[r - 2][c]
						    .size() >= 2)
					    || (possibilities[r - 2][c + 1]
						    .contains(num) && possibilities[r - 2][c + 1]
						    .size() >= 2) || (possibilities[r - 2][c + 2]
					    .contains(num) && possibilities[r - 2][c + 2]
					    .size() >= 2))) {
				for (int i = 0; i < 9; i++) {
				    if (i != c && i != c + 1 && i != c + 2) {
					// System.out.println("Removing " + num
					// + " from [" + r + "][" + i + "]");
					possibilities[r][i].remove(new Integer(
						num));
					if (possibilities[r][i].size() == 1) {
					    eliminate(r, i, possibilities[r][i]
						    .get(0));
					    deleteRCSDuplicate(r, i,
						    possibilities[r][i].get(0));
					}
				    }
				}
			    }
			}
		    }
		}
	    }
	    findRowColRemains();
	    if (filledCells == tempFilledCells) // to avoid infinite loop
		break;
	}
	if (oldFilledCount == filledCells)
	    System.out.println("No progress made by phase 4.....");
	else if (oldFilledCount < filledCells) {
	    int diff = filledCells - oldFilledCount;
	    System.out.println("No. of boxes filled by phase 4: " + diff);
	}
    }
}
